#include <iostream>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <string>
#include "Task.hpp"

class Channel
{
private:
    std::string _name;
    int _wfd;
    pid_t _subprocessid;
public:
    Channel(std::string& name, int wfd, pid_t id)
        :_name(name), _wfd(wfd), _subprocessid(id)
    { }
    void CloseChannel() { close(_wfd); }
    void Wait()
    {
        pid_t rid = waitpid(_subprocessid, nullptr, 0);
        if(rid > 0)
        {
            std::cout<<"wait: "<<rid<<" success!"<<std::endl;
        }
    }
    std::string GetName() { return _name; }
    int GetWfd() { return _wfd; }
    pid_t GetSubProcessId() { return _subprocessid; }
    ~Channel() { }
};

void CreateChannelAndSub(int num, std::vector<Channel>* channels, task_t work)
{
    for(int i=0;i<num;i++)
    {
        int pipefd[2];
        int n = pipe(pipefd);
        if(n<0) exit(1);

        pid_t id = fork();
        if (id==0)
        {
            //childs
            close(pipefd[1]);
            dup2(pipefd[0],0);
            work();
            close(pipefd[0]);
            exit(0);
        }
        //father
        std::string name = "Channel_" + std::to_string(i);
        close(pipefd[0]);
        channels->push_back(Channel(name,pipefd[1],id));
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}


void CtrlProcessOnce(std::vector<Channel> channels)
{
    int n = NextChannel(channels.size());
    int taskCommand = SelectCommand();
    write(channels[n].GetWfd(),&taskCommand,sizeof(int));
    std::cout<<"taskCommand: "<<taskCommand<<"  Channel_name: "<<channels[n].GetName()
             <<"  Sub_Process: "<<channels[n].GetSubProcessId()<<std::endl;
    sleep(1);
}

void CtrlProcess(std::vector<Channel> channels, int times = -1)
{
    if(times == -1)
    {
        while ((1)) CtrlProcessOnce(channels);
    }
    else
    {
        while(times--) CtrlProcessOnce(channels);
    }
}

void CleanUpChannel(std::vector<Channel> channels)
{
    for(auto& channel : channels)
    {
        channel.CloseChannel();
    }
    for(auto& channel : channels)
    {
        channel.Wait();
    }
}

int main(int argc, char* argv[])
{
    if(argc != 2 || argc != 3) 
    {
        std::cerr<<"Error! Two or Three parameters must be entered!"<<std::endl;
    }
    int CommandNums = (argc==3)?std::stoi(argv[2]):10;
    
    int num = std::stoi(argv[1]);
    LoadTask();
    std::vector<Channel> channels;

    CreateChannelAndSub(num, &channels, work);

    CtrlProcess(channels, CommandNums);

    CleanUpChannel(channels);

    return 0;
}